package org.balthie.cal.common.spec;

import org.balthie.cal.common.ex.InputException;
import org.balthie.cal.common.ex.NotImplementedException;

/**
 * @author：balthie@126.com
 * @createtime ： 2022年4月27日 上午10:56:29
 * @description <b> 规格</b>："规格"是为特殊业务逻辑显式申明的ValueObject
 *              <p>
 *              业务规则通常不适合作为Entity或
 *              ValueObject的职责，而且规则的变化和组合也会掩盖领域对象的基本含义。但是将规则移出领域层的结果会更糟糕，因为这样一来领域代码就不在表达模型了。
 *              <p>
 *              DDD提供了一种“规格”概念，即“谓语”这种可分离、可组合的规则对象。“规格”中申明的是限制另一个对象状态的约束，被约束的对象可以存在，也可以不存在。因此：
 *              <b> "规格"是为特殊（指定）业务逻辑显式申明的ValueObject</b>,
 *              用来对规则排列组合以确定对象是否满足某些标准。
 *              <p>
 *              “规格”的应用：出于以下3个目的中的至少一个，我们就可以使用“规格”
 *              <table>
 *              <tr>
 *              <td>1</td>
 *              <td>验证对象，检查它是否能满足某些业务规则或符合某些状态</td>
 *              <td>return true or false</td>
 *              </tr>
 *              <tr>
 *              <td>2</td>
 *              <td>从集合中选择（或查询）一个对象（如：查询过期发票）</td>
 *              <td>return Entity</td>
 *              </tr>
 *              <tr>
 *              <td>3</td>
 *              <td>指定在创建新对象时，必须满足某些条件</td>
 *              <td>throw 业务校验异常</td>
 *              </tr>
 *              </table>
 * 
 * @since version 初始于版本 3.1.3
 */
public interface ISpecification<EntityConcreteType> {
	public static final ISpecification EMPTY_SPEC = new EmptySpecification();

	/**
	 * @author：balthie@126.com
	 * @createtime ： 2022年4月27日 下午1:57:19
	 * @description 验证对象，检查它是否能满足某些业务规则或符合某些状态，不抛出业务异常
	 * @since version 初始于版本 3.1.3
	 * @param candidate 待验证的entity
	 * @return
	 */
	boolean isSatisfiedBy(EntityConcreteType candidate);

	/**
	 * Create a new specification that is the AND operation of {@code this}
	 * specification and another specification.
	 * 
	 * @param specification Specification to AND.
	 * @return A new specification.
	 */
	default ISpecification<EntityConcreteType> and(ISpecification<EntityConcreteType> specification) {

		throw new NotImplementedException("子类未提供实现");
	}

	/**
	 * Create a new specification that is the OR operation of {@code this}
	 * specification and another specification.
	 * 
	 * @param specification Specification to OR.
	 * @return A new specification.
	 */
	ISpecification<EntityConcreteType> or(ISpecification<EntityConcreteType> specification);

	/**
	 * Create a new specification that is the NOT operation of {@code this}
	 * specification.
	 * 
	 * @param specification Specification to NOT.
	 * @return A new specification.
	 */
	default ISpecification<EntityConcreteType> not(ISpecification<EntityConcreteType> specification) {
		throw new NotImplementedException("子类未提供实现");
	}

	/**
	 * @author：balthie@126.com
	 * @createtime ： 2022年4月27日 下午1:57:19
	 * @description 验证内存对象规格，并抛出业务异常
	 * @since version 初始于版本 3.1.3
	 */
	default void check(EntityConcreteType candidate) {
		if (!this.isSatisfiedBy(candidate)) {
			throw new InputException(getTips());
		}
	}

	/**
	 * @author：balthie@126.com
	 * @createtime ： 2023年3月11日 下午1:57:19
	 * @description 不满足业务规格时的友好提示
	 * @since version 初始于版本 3.1.3
	 */
	default String getTips() {
		return String.format("不满足业务规格: %s", this.getClass().getTypeName());
	}

	static class EmptySpecification<T> extends AbstractSpecification<T> {
		private EmptySpecification() {
			super();
		}

		@Override
		public boolean isSatisfiedBy(T candidate) {
			return true;
		}
	}
}
